package com.company.cloud.common.redis.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.Record;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.connection.stream.StringRecord;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StreamOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;
import com.company.cloud.common.cache.CacheService;

@Component
public class RedisUtils implements CacheService{

	/** 默认过期时长，单位：秒 */
	public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
	/** 不设置过期时长 */
	public final static long NOT_EXPIRE = -1;

	@Autowired
//	@Qualifier("redisTemplate")
	private RedisTemplate<String, Object> redisTemplate;

	@Resource(name = "redisTemplate")
	private ValueOperations<String, String> valueOperations;

	@Resource(name = "redisTemplate")
	private HashOperations<String, String, Object> hashOperations;

	@Resource(name = "redisTemplate")
	private ListOperations<String, Object> listOperations;

	@Resource(name = "redisTemplate")
	private SetOperations<String, Object> setOperations;

	@Resource(name = "redisTemplate")
	private ZSetOperations<String, Object> zSetOperations;
	
//	@Resource(name = "redisTemplate")
//	@Autowired
//	private StreamOperations<String, String, String> streamOperations;
	
	
	@Deprecated
	private Object getVal(Set<Object> sets,int idx) {
		if(sets==null || sets.size()<=0) {
			return null;
		}
		int i=1;
		for (Object val:sets) {
			if(i==idx) {
				return val;
			}
			i++;
		}
		return null;
	}
	
	//--------stream
//	public String xadd(String key,Map<String, String> content) {
//		RecordId id= streamOperations.add(key, content);
//		return id.getValue();
//	}
//	
//	public String xadd(Map<String, String> raw) {
////		MapRecord<String, String, String> m1= StreamRecords.mapBacked(raw);
////		RecordId id=streamOperations.add(m1);
//		
//		
//		StringRecord record=StreamRecords.string(raw);
//		record.withStreamKey("a1");
//		RecordId id=streamOperations.add(record);
//		return id.getValue();
//		
//	}
//	
//	
//	public List<MapRecord<String, String, String>> xread1(String key) {
//		StreamOffset<String> s=StreamOffset.latest(key);
//		List<MapRecord<String, String, String>> list=streamOperations.read(s);
//		return list;
//	}
//	
//	public void xread2(String key) {
//		StreamOffset<String> s=StreamOffset.create(key, ReadOffset.lastConsumed());
////		streamOperations.read(streams)
////		Consumer c=Consumer.from(group, name)
////		streamOperations.read(consumer, streams)
//	}
	
	
	//--------zset(有序)
	public boolean zsadd(String key,Object value,int score) {
		if(value==null) {
			return false;
		}
		return zSetOperations.add(key,this.toJson(value), score);
	}

	public String zsget(String key,int score) {
		Set<Object> vals= zSetOperations.rangeByScore(key, score, score);
		Object obj= this.getVal(vals, 1);
		return obj==null?null:(String)obj;
	}
	
	//--------set
	public Long sadd(String key ,Set<String> values,long expire) {
		Long num= setOperations.add(key, values.toArray());
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return num;
	}
	
	public void sadd(Map<String, Set<String>> keyValMap,long expire) {
		keyValMap.forEach((key,values)->{
			this.sadd(key, values, expire);
		});
	}
	
	public void sadd(Map<String, Set<String>> keyValMap) {
		for(String key:keyValMap.keySet()) {
			Set<String> valset= keyValMap.get(key);
			this.sadd(key, valset);
		}
	}
	
	public Long sadd(String key ,String value) {
		Long num= setOperations.add(key, value);
		return num;
	}
	
	public Long sadd(String key ,String... values) {
		Long num= setOperations.add(key, values);
		return num;
	}
	
	public Long sadd(String key ,Set<String> values) {
		return sadd(key, values,NOT_EXPIRE);
	}
	
	public Set<Object> smembers(String key) {
	 	return setOperations.members(key);
	}
	
	public boolean sisMember(String key,Object value) {
		return setOperations.isMember(key, value);
	}
	
	public void sremove(String key,String value) {
		setOperations.remove(key, value);
	}
	
	public void sremove(String key,Set<Object> values) {
		setOperations.remove(key, values);
	}
	
	public Long ssize(String key) {
		return setOperations.size(key);
	}
	
	
	//--------list
	public long llpush(String key,Object value,long expire) {
		if(value==null ) {
			return -1;
		}
		long l=listOperations.leftPush(key,toJson(value));
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return l;
	}
	
	public long llpush(String key,List<String> values) {
		return listOperations.leftPushAll(key, values);
	}
	
	public long lrpush(String key,Object value,long expire) {
		if(value==null ) {
			return -1;
		}
		long l= listOperations.rightPush(key,toJson(value));
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return l;
	}
	
	public long lrpush(String key,List<String> values) {
		return listOperations.rightPushAll(key, values);
	}
	
	public Object llpop(String key) {
		return listOperations.leftPop(key);
	}
	
	public Object lrpop(String key) {
		return listOperations.rightPop(key);
	}
	
	public void llpop(String key,long timeout) {
		listOperations.leftPop(key, timeout, TimeUnit.SECONDS);
	}
	
	public void lrpop(String key,long timeout) {
		listOperations.rightPop(key, timeout, TimeUnit.SECONDS);
	}
	
	public long lsize(String key) {
		return listOperations.size(key);
	}
	
	public List<Object> lrange(String key,long start,long end) {
		return listOperations.range(key, start, end);
	}
	
	public void ltrim(String key,long start,long end) {
		listOperations.trim(key, start, end);
	}
	
	
	//--------map
	public void mput(String key,String hashKey,Object value) {
		this.mput(key, hashKey, value, NOT_EXPIRE);
	}
	
	public void mput(String key,String hashKey,Object value,long expire) {
		if(value==null ) {
			return;
		}
		hashOperations.put(key, hashKey, toJson(value));
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
	}
	
	public void mput(String key,Map<String, String> vals,long expire) {
		hashOperations.putAll(key, vals);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
	}
	
	public void mput(String key,Map<String, String> vals) {
		this.mput(key, vals,NOT_EXPIRE);
	}

	public Object mget(String key,String hashKey) {
		return hashOperations.get(key, hashKey);
	}
	
	public <T> T mget(String key,String hashKey, Class<T> clazz) {
		return this.mget(key, hashKey, clazz, NOT_EXPIRE);
	}
	
	public boolean mhasKey(String key,String hashKey) {
		return hashOperations.hasKey(key, hashKey);
	}
	
	
	public long mincrement(String key,String hashKey) {
		 return hashOperations.increment(key, hashKey,1);
	}
	
	public long mincrement(String key,String hashKey,long delta) {
		 return hashOperations.increment(key, hashKey,delta);
	}
	
	
	public <T> List<T> mget(String key,List<String> hashKeys,Class<T> clazz) {
		List<Object> list= hashOperations.multiGet(key, hashKeys);
		List<T> result=new ArrayList<T>();
		if(CollectionUtils.isEmpty(list)) {
			list.forEach(obj->{
				result.add(fromJson(obj.toString(), clazz));
				
			});
		}
		return result;
	}
	
	public List<String> mget(String key,Set<String> hashKeys){
		List<Object> list= hashOperations.multiGet(key, hashKeys);
		List<String> result=new ArrayList<String>();
		if(!CollectionUtils.isEmpty(list)) {
			list.forEach(obj->{
				if(obj!=null) {
					result.add(obj.toString());
				}
			});
		}
		return result;
	}
	
	public <T> List<T> mget(String key,Set<String> hashKeys,Class<T> clazz) {
		List<Object> list= hashOperations.multiGet(key, hashKeys);
		List<T> result=new ArrayList<T>();
		if(CollectionUtils.isEmpty(list)) {
			list.forEach(obj->{
				result.add(fromJson(obj.toString(), clazz));
				
			});
		}
		return result;
	}
	
	
	
	public <T> T mget(String key,String hashKey, Class<T> clazz,long expire) {
		Object obj= hashOperations.get(key, hashKey);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return obj == null ? null : fromJson(obj.toString(), clazz);
	}
	
	public Map<String, Object> mget(String key) {
		return hashOperations.entries(key);
	}
	
	public long mdel(String key,String hashKey) {
		return hashOperations.delete(key, hashKey);
	}
	
	public long mdel(String key,List<String> hashKeys) {
		return hashOperations.delete(key, hashKeys);
	}
	
	public long mdel(String key,Set<String> hashKeys) {
		return hashOperations.delete(key, hashKeys);
	}
	
	public Set<String> mkeys(String key) {
		return hashOperations.keys(key);
	}
	
	public List<Object> mValues(String key) {
		return hashOperations.values(key);
	}
	
	//--------string
	public void multiSet(Map<String, String> map) {
		if(CollectionUtils.isEmpty(map)) {
			return ;
		}
		valueOperations.multiSet(map);
	}
	
	public void multiSet(Map<String, String> map, long expire) {
		if(CollectionUtils.isEmpty(map)) {
			return ;
		}
		valueOperations.multiSet(map);
		if (expire != NOT_EXPIRE) {
			for(String key:map.keySet()) {
				redisTemplate.expire(key, expire, TimeUnit.SECONDS);
			}
		}
	}
	
	public void set(String key, Object value, long expire) {
		if(value==null) {
			return ;
		}
		valueOperations.set(key, toJson(value));
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
	}

	public void set(String key, Object value) {
		if(value==null) {
			return ;
		}
		set(key, value, NOT_EXPIRE);
	}
	
	/**原子增*/
	public Long increment(String key,Long delta) {
		return valueOperations.increment(key, delta);
	}
	/**原子增*/
	public Long increment(String key) {
		return valueOperations.increment(key,1);
	}
	/**原子减*/
	public Long decrement(String key,Long delta) {
		return valueOperations.decrement(key, delta);
	}
	/**原子减*/
	public Long decrement(String key) {
		return valueOperations.decrement(key);
	}

	public <T> T get(String key, Class<T> clazz, long expire) {
		String value = valueOperations.get(key);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return value == null ? null : fromJson(value, clazz);
	}

	public <T> T get(String key, Class<T> clazz) {
		return get(key, clazz, NOT_EXPIRE);
	}

	public String get(String key, long expire) {
		String value = valueOperations.get(key);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return value;
	}

	public String get(String key) {
		return get(key, NOT_EXPIRE);
	}

	public List<String> getBatchList(Set<String> keys) {
		List<String> values = valueOperations.multiGet(keys);
		return values;
	}

	public Map<String, String> getBatchMap(Set<String> keys) {
		List<String> values = getBatchList(keys);
		Map<String, String> result = new LinkedHashMap<>();
		if (!CollectionUtils.isEmpty(keys)) {
			int index = 0;
			for (String key : keys) {
				result.put(key, values.get(index++));
			}
		}
		return result;
	}

	public void delete(String key) {
		redisTemplate.delete(key);
	}

	public void delete(Collection<String> keys) {
		redisTemplate.delete(keys);
	}
	
	
	public Set<String> keys(String pattern) {
		return redisTemplate.keys(pattern);
	}
	
	public boolean hasKey(String key) {
		return redisTemplate.hasKey(key);
	}
	
	/**
	 * Object转成JSON数据
	 */
	private String toJson(Object object) {
		if(object==null) {
			return null;
		}
		if (object instanceof Integer || object instanceof Long || 
				object instanceof Float || object instanceof Double
				|| object instanceof Boolean || object instanceof String) {
			return String.valueOf(object);
		}
		return JSON.toJSONString(object);
	}

	/**
	 * JSON数据，转成Object
	 */
	private <T> T fromJson(String json, Class<T> clazz) {
		return JSON.parseObject(json, clazz);
	}
}
